
#include <ilcplex/ilocplex.h>
#include <ilcplex/ilocplexi.h>
#include <cstdio>
#include <list>

using namespace std;

int bestIncumbent = 1;



ILOMIPINFOCALLBACK3(loggingCallback,
                    IloNumVarArray, vars,
                    IloNum,         lastLog, 
                    IloNum,         lastIncumbent)
{
   int newIncumbent = 0;
   int nodes = getNnodes();
	 if(hasIncumbent() )
		 bestIncumbent = getIncumbentObjValue();
	 if(false){
   if ( hasIncumbent()                                  && 
        fabs(lastIncumbent - getIncumbentObjValue())
              > 1e-5*(1.0 + fabs(getIncumbentObjValue())) ) {
      lastIncumbent = getIncumbentObjValue();
      newIncumbent = 1;
   }
     
   if ( nodes >= lastLog + 100  ||  newIncumbent ) {  

      if ( !newIncumbent )  lastLog = nodes;
      getEnv().out() << "Nodes = " << nodes
                     << '(' << getNremainingNodes() << ')'
                     << "  Best objective = " << getBestObjValue();

      if ( hasIncumbent() ) {
         getEnv().out() << "  Incumbent objective = " << getIncumbentObjValue()
                        << endl;
      }
      else {
         getEnv().out() << endl;
      }

   }
   if ( newIncumbent ) {
      IloNumArray val(vars.getEnv());
      getIncumbentValues(val, vars);
      val[0] = getIncumbentValue(vars[0]);
      getEnv().out() << "New incumbent variable values: " << endl
                     << val << endl;
      val.end();
   }
	}
}

enum CutMode{ CLQ2A, CLQ2B };

bool getCut( IloNumArray& vals, IloNumVarArray& vars, CutMode cutmode, int set, IloRange* cut, int** graph ){
	int n = vals.getSize()/2;
	bool marked[n];
	for( int i = 0; i < n; ++i )
		marked[i] = false;
	
	int winner;
	int count_marked = 0;
	list<int> indices;
	
	while( count_marked < n ){
		//choose the star node
		winner = -1;
		for( int i = 0; i < n; ++i ){
			if( !marked[i] ){
				if( winner == -1 ){
					winner = i;
				}
				//veriricar se é inteira !comparação com double
				else if( cutmode == CLQ2B && fabs(vals[i+n*set]-0.5) < fabs(vals[winner+n*set]-0.5) && vals[i+n*set] > 0 && vals[i+n*set] < 1 ){
					winner = i;
				}
				else if( cutmode == CLQ2A && vals[i+n*set] > vals[winner+n*set] && vals[i+n*set] < 1 ){
					winner = i;
				}
			}
		}
		
		++count_marked;
		marked[winner] = true;
		indices.push_back( winner+n*set );
		
		for( int i = 0; i < n; ++i ){
			if( !graph[i][winner] ){
				if( !marked[i] ){
					marked[i] = true;
					++count_marked;
				}
			}
		}
	}
	
	list<int>::iterator it = indices.begin();
	float sum = 0;
	while( it != indices.end() ){
		sum += vals[*it];
		++it;
	}
	
	if( sum > 1 ){
		it = indices.begin();
		while( it != indices.end() ){
			cut->setLinearCoef( vars[*it], 1 );
			++it;
		}
			
		return true;
	}
	return false;
}



ILOUSERCUTCALLBACK2( CtCallback, IloNumVarArray, vars, int**, graph ) {
	
	IloNumArray vals(getEnv());
	getValues(vals, vars);

	IloRange cut( getEnv(), 0, 1);
	static int cont=0;
	for( int i = 0; i < 2; ++i ){

		if( getCut( vals, vars, CLQ2B, i, &cut, graph ) ){
			add(cut);
		}
		else if( getCut( vals, vars, CLQ2A, i, &cut, graph ) ){
			add(cut);
		}
	}
}

enum RandMode{RAND1, RAND2};

int getFeasible(IloEnv env, IloNumVarArray& vars, IloNumArray& vals, IloCplex::ControlCallbackI::IntegerFeasibilityArray feas, RandMode mode, int *newx, int** graph){
	
	printf("%d\n", __LINE__);
	int n = vals.getSize()/2;

	
	


	int newobj = 0;
	memset(newx, 0, 2*n*sizeof(int));
	for( int set = 0; set < 2; ++set ){
		bool marked[n];
		int count_marked = 0;
		for( int i = 0; i < n; i++ ){
			if(feas[i+n*set] != 0/*IloCplex::ControlCallbackI::IntegerFeasibility*/){
				marked[i] = false;
			}else{
				marked[i]=true;
				count_marked++;
			}
		}
	printf("%d\n", __LINE__);
		int winner;
		printf("%d\n", __LINE__);
		while( count_marked < n ){
			//choose the star node
			winner = -1;
			for( int i = 0; i < n; ++i ){
				if( !marked[i] ){
					if( winner == -1  ){
						winner = i;
					}
					else if( vals[i+n*set] > vals[winner+n*set] ){
						winner = i;
					}
				}
			}
			printf("%d\n", __LINE__);
			marked[winner] = true;
			++count_marked;
			if(set==1 && newx[winner]==1){
				continue;
				//newx[winner+n*set] = 0;
				//marked[winner]=true;
			}
			
			newx[winner+n*set] = 1;
			marked[winner]=true;
			//printf("%d\n", winner);
			newobj++;
			for( int i = 0; i < n; ++i ){
				if( graph[i][winner] ){
					if( !marked[i] ){
						marked[i] = true;
						if(newx[i+n*set]==1){
					}
						newx[i+n*set] = 0;
						++count_marked;
					}
				}
			}
			
			int temp = 0;
			for( int i = 0; i < n; i++)
				temp+=marked[i];
			if( temp != count_marked ){
				printf("error %d %d\n", temp, count_marked);
				exit(-1);
			}
			
		}
		
	}
	return newobj;

}

ILOHEURISTICCALLBACK2(Rounddown, IloNumVarArray, vars, int**, graph) {
	
	printf("OIDOASIDOASDI %d\n", __LINE__);
	fflush(stdout);

	IloNumArray vals(getEnv());
	getValues(vals, vars);

	printf("%d\n", __LINE__);
	IloNumArray x(getEnv());

	IntegerFeasibilityArray feas(getEnv());
	getFeasibilities( feas, vars );
	getValues( x, vars );

		printf("%d\n", __LINE__);

	int n = vars.getSize()/2;
	//printf("n = %d\n", n);
	//IntegerFeasibilityArray feas( getEnv() );
	//IloNumArray x( getEnv() );
	//printf("%p\n", getEnv());
	//getFeasibilities( feas, vars );
	//getValues( x, vars );
	int xRand1[2*n];
	int xRand2[2*n];

	int valRand1 = getFeasible(getEnv(), vars, vals, feas, RAND1, xRand1, graph);
	int valRand2 = getFeasible(getEnv(), vars, vals, feas, RAND1, xRand2, graph);

	int valWin = max(valRand1, valRand2);
	int *xWin = NULL;
	if(valRand1>valRand2){
		valWin = valRand1;
		xWin = xRand1;
	}else{
		valWin = valRand2;
		xWin = xRand2;
	}
	
	if(valWin > bestIncumbent){
		bestIncumbent = valWin;
		int cont=0;
		for(int i=0; i<2*n; i++){
			x[i] = xWin[i];
			cont += x[i];
		}
		if(cont != valWin){
			printf("ERRO LINHA %d\n", __LINE__);
		}
		setSolution( vars, x, valWin );
  	
    x.end();
	}
}

